package com.example.spring_java_oj.model;

import com.example.spring_java_oj.Util.CommandUtil;
import com.example.spring_java_oj.Util.FileUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class Task {
    //通过一组常量约定临时文件的名字
    //这个表示所有临时文件所在的目录
    private String WORK_DIR = null;
    //约定代码的类名
    private String CLASS = null;
    //约定要编译的代码文件名
    private String CODE = null;
    //约定存放编译错误信息的文件名
    private String COMPILE_ERROR = null;
    //约定存放运行时的标准输出的文件名
    private String STDOUT = null;
    //约定存放运行时的标准错误的文件名
    private String STDERR = null;

    public Task(Integer id, HttpServletRequest request){
        HttpSession session = request.getSession();
        WORK_DIR = "./tmp/" + session.getAttribute("username") + id + "/";
        CLASS = "Solution";
        CODE = WORK_DIR + "Solution.java";
        COMPILE_ERROR = WORK_DIR + "compileError.txt";
        STDOUT = WORK_DIR + "stdout.txt";
        STDERR = WORK_DIR + "stderr.txt";
    }

    //这个Task类提供的核心方法，就叫做compileAndRun，编译+运行的意思
    //参数：要编译运行的java源代码
    //返回值：表示编译运行的结果
    public Answer compileAndRun(String code){
        Answer answer = new Answer();
        File workDir = new File(WORK_DIR);
        if (!workDir.exists()){
            workDir.mkdirs();
        }

        if(!checkCodeSafe(code)){
            System.out.println("用户提交了不安全的代码！");
            answer.setError(3);
            answer.setReason("你提交的代码可能会对服务器造成危害，禁止运行!");
            return answer;
        }

        //1.把question中的code写入到一个Solution。java文件中
        FileUtil.writeFile(CODE, code);
        //2.创建子进程，调用javac进行编译。注意！编译的时候，需要有一个。java文件
        //如果编译出错，javac就会把错误信息写入到stderr里，就可以用一个专门的文件来保存。compileError.txt
        String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
        System.out.println(compileCmd);
        CommandUtil.run(compileCmd, null, COMPILE_ERROR);
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")){
            System.out.println("编译错误");
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }
        //3.创建子进程，调用java命令并执行
        //运行程序的时候，也会把java子进程的标准输出和标准错误获取到stdout.txt,stderr.txt
        String runCmd = String.format("java -classpath %s %s", WORK_DIR, CLASS);
        System.out.println(runCmd);
        CommandUtil.run(runCmd, STDOUT, STDERR);
        String runError = FileUtil.readFile(STDERR);
        if (!runError.equals("")){
            System.out.println("运行出错");
            answer.setError(2);
            answer.setReason(runError);
            return answer;
        }
        //4。父进程获取到刚才的编译执行的结果，并打包成Answer对象
        //编译执行的结果，就通过刚才约定的这几个文件进行获取即可

        answer.setError(0);
        answer.setStdout(FileUtil.readFile(STDOUT));
        return answer;
    }

    private boolean checkCodeSafe(String code) {
        List<String> blackList = new ArrayList<>();
        blackList.add("Runtime");
        blackList.add("exec");
        blackList.add("java.io");
        //禁止提交的代码访问网络
        blackList.add("java.net");
        for(String target : blackList){
            int pos = code.indexOf(target);
            if (pos >= 0){
                return false;
            }
        }
        return true;
    }


}
